;; https://projecteuler.net/problem=27

;; Number spiral diagonals
;; Problem 28

;; Starting with the number 1 and moving to the right in a clockwise
;; direction a 5 by 5 spiral is formed as follows:

;; 21 22 23 24 25
;; 20  7  8  9 10
;; 19  6  1  2 11
;; 18  5  4  3 12
;; 17 16 15 14 13

;; It can be verified that the sum of the numbers on the diagonals is
;; 101.

;; What is the sum of the numbers on the diagonals in a 1001 by 1001
;; spiral formed in the same way?

(import
 (except (rnrs base) let-values map)
 (only (guile)
       lambda* λ)
 (contract)
 (prefix (lib math) math:)
 (lib print-utils))


;; A square of odd width filled with the numbers from 1 to some
;; number, building up from the center as described, will have a
;; square number in one of the corners.

;; To get the numbers in the other corners, one can substract the
;; width of the square reduced by 1 from the previous corner:

;; c_1 = w^2 = w^2 - 0(w - 1)
;; c_2 = w^2 - 1(w - 1)
;; c_3 = w^2 - 2(w - 1)
;; c_4 = w^2 - 3(w - 1)

;; This results in the following function:

(define-with-contract corner-num
  (require (integer? width)
           (math:odd? width)
           (integer? corner-ind)
           (and (>= corner-ind 0)
                (<= corner-ind 3)))
  (ensure (integer? <?>))
  (λ (width corner-ind)
    (- (math:square width)
       (* corner-ind
          (- width 1)))))


(define-with-contract corner-sum
  (require (integer? width)
           (math:odd? width))
  (ensure (integer? <?>)
          (or (= <?> 1)
              (> <?> width)))
  (λ (width)
    (cond
     [(= width 1) 1]
     [else
      (+ (corner-num width 0)
         (corner-num width 1)
         (corner-num width 2)
         (corner-num width 3))])))


(print "solution:"
       (let iter ([width 1001] [sum 0])
         (cond
          [(= width 1)
           (+ sum (corner-sum width))]
          [else
           (iter (- width 2) (+ sum (corner-sum width)))])))
